package com.soundplus.app.shp04.DeviceConnectMgr;


import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.util.Log;

import com.example.sppmode.Common.MyByteQueue;
import com.example.sppmode.Common.Utils;

import java.util.UUID;

public class BleConnMgr {
    private static final String TAG = "BTLeConnMgr";

    public static final UUID BLE_SERVICE_UUID = UUID.fromString("01005053-0000-1000-8000-00805F9B34FB");
    public static final UUID DEV_TX_CHAR_UUID = UUID.fromString("02005053-0000-1000-8000-00805F9B34FB");
    public static final UUID DEV_RX_CHAR_UUID = UUID.fromString("03005053-0000-1000-8000-00805F9B34FB");
    public static final UUID DEV_TX_NOTIFI_UUID = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB");


//    public static final UUID BLE_SERVICE_UUID = UUID.fromString("01000100-0000-1000-8000-009078563412");
//    public static final UUID DEV_TX_CHAR_UUID = UUID.fromString("02000200-0000-1000-8000-009178563412");
//    public static final UUID DEV_RX_CHAR_UUID = UUID.fromString("03000300-0000-1000-8000-009278563412");
//    public static final UUID DEV_TX_NOTIFI_UUID = UUID.fromString("00002902-0000-1000-8000-009178563412");

    public static final int BLE_DISCONNECTED = 0;
    public static final int BLE_CONNECTING = 1;
    public static final int BLE_CONNECTED = 2;

    private Context context;
    private BluetoothAdapter bluetoothAdapter;
    private BleEventListener bleEventListener;
    private BluetoothGatt bluetoothGatt = null;
    private int connStatus = BluetoothGatt.STATE_DISCONNECTED;
    private boolean canSendData = false;
    private boolean bSending = false;
    private SendThread sendThread;
    boolean bSendThreadRunning = false;

    public BleConnMgr(Context context, BleEventListener listener) {
        this.context = context;
        this.bleEventListener = listener;


        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Log.e(TAG, "BTLeConnMgr can not get bluetoothAdapter");
        }
        Log.d(TAG, "BLE_CONN_STEP 01 getDefaultAdapter success");


    }

    public void destroy() {

    }

    @SuppressLint("MissingPermission")
    public boolean connectDevice(final String addr) {
        Log.d(TAG, "BLE_CONN_STEP 02 connectDevice addr:" + addr);

        if (addr == null) {
            Log.d(TAG, "connectDevice return addr==null");
            return false;
        }

        final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(addr);
        if (device == null) {
            Log.d(TAG, "connectDevice return device==null");
            return false;
        }
        Log.d(TAG, "BLE_CONN_STEP 03 connectDevice.getRemoteDevice success");

        bluetoothGatt = device.connectGatt(context, true, bluetoothGattCallback);
        if (bluetoothGatt == null) {
            Log.d(TAG, "connectDevice return connectGatt bluetoothGatt==null");
        }

        Log.d(TAG, "BLE_CONN_STEP 04 connectDevice.connectGatt");
        updateConnectStatus(BLE_CONNECTING);
        return true;
    }

    @SuppressLint("MissingPermission")
    public void disconnectDevice() {
        if (bluetoothGatt != null) {
            bluetoothGatt.disconnect();
        }
    }

    private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
        }

        @SuppressLint("MissingPermission")
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.d(TAG, "BLE_CONN_STEP 05 onConnectionStateChange status=" + status + " newState=" + newState);

            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
                Log.d(TAG, "BLE_CONN_STEP 06 onConnectionStateChange connect Gatt success");
                updateConnectStatus(BLE_CONNECTED);

                Log.d(TAG, "BLE_CONN_STEP 07 onConnectionStateChange discoverServices");
                boolean ret = bluetoothGatt.discoverServices();
                Log.d(TAG, "onConnectionStateChange ret=" + ret);

            } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
                Log.d(TAG, "BLE_CONN_STEP 14 onConnectionStateChange Gatt disconnected");
                updateConnectStatus(BLE_DISCONNECTED);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(TAG, "BLE_CONN_STEP 08 onServicesDiscovered onServicesDiscovered");

            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, "BLE_CONN_STEP 09 onServicesDiscovered onServicesDiscovered success");
                enableDeviceTxNotification();
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.d(TAG, "onCharacteristicRead status = " + status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //receiveData(characteristic);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            Log.d(TAG, "onCharacteristicChanged");
            Log.d(TAG, "characteristic="+characteristic.getUuid().toString());
            receiveData(characteristic);
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            if (descriptor.getUuid().compareTo(DEV_TX_NOTIFI_UUID) == 0) {
                Log.d(TAG, "onDescriptorWrite status = " + status);

                if (status == BluetoothGatt.GATT_SUCCESS)
                    canSendData = true;
                else
                    canSendData = false;
            }
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            Log.d(TAG, "onReliableWriteCompleted status = " + status);
            bSending = false;
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
        }


    };

    private void updateConnectStatus(int status) {
        connStatus = status;
        if (bleEventListener != null) {
            bleEventListener.onConnStatusChanged(status);
        }
    }

    @SuppressLint("MissingPermission")
    private boolean enableDeviceTxNotification() {
        Log.d(TAG, "BLE_CONN_STEP 10 enableDeviceTxNotification");

        BluetoothGattService gattServer = bluetoothGatt.getService(BLE_SERVICE_UUID);
        if (gattServer == null) {
            Log.d(TAG, "enableDeviceTxNotification return gattServer == null");
            return false;
        }
        Log.d(TAG, "BLE_CONN_STEP 11 enableDeviceTxNotification getService success");

        BluetoothGattCharacteristic devTxChar = gattServer.getCharacteristic(DEV_TX_CHAR_UUID);
        if (devTxChar == null) {
            Log.d(TAG, "enableDeviceTxNotification return devTxChar == null");
            return false;
        }

        bluetoothGatt.setCharacteristicNotification(devTxChar, true);
        BluetoothGattDescriptor descriptor = devTxChar.getDescriptor(DEV_TX_NOTIFI_UUID);
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        bluetoothGatt.writeDescriptor(descriptor);
        Log.d(TAG, "BLE_CONN_STEP 13 enableDeviceTxNotification writeDescriptor success");
        return true;
    }

    @SuppressLint("MissingPermission")
    private boolean writeDevRxChar(final byte[] data) {
        Log.d(TAG, "sendData");

        if (data == null || data.length == 0) {
            Log.d(TAG, "sendData return data == null || data.length == 0");
            return false;
        }

        if (bluetoothGatt == null) {
            Log.d(TAG, "sendData return bluetoothGatt == null");
            return false;
        }

        BluetoothGattService gattServer = bluetoothGatt.getService(BLE_SERVICE_UUID);
        if (gattServer == null) {
            Log.d(TAG, "sendData gattServer == null");
            return false;
        }

        BluetoothGattCharacteristic devRxChar = gattServer.getCharacteristic(DEV_RX_CHAR_UUID);
        if (devRxChar == null) {
            Log.d(TAG, "sendData return devRxChar == null");
            return false;
        }

        devRxChar.setValue(data);
        boolean status = bluetoothGatt.writeCharacteristic(devRxChar);
        Log.d(TAG, "writeDevRxChar status=" + status);
        return true;
    }

    private MyByteQueue sendQueue = new MyByteQueue("sendQueue", 1024);

    public void sendData(final byte[] data) {
        if (data == null || data.length == 0) {
            Log.d(TAG, "sendData return data == null || data.length == 0");
            return;
        }

        sendQueue.enQueue(data);
        if (!bSendThreadRunning) {
            new SendThread().start();
        }

    }

    private class SendThread extends Thread {

        @Override
        public void run() {
            Log.d(TAG, "SendThread run");

            bSendThreadRunning = true;
            while (bSendThreadRunning) {
                byte[] data = sendQueue.deQueue(20);
                if (data != null) {
                    bSending = true;
                    writeDevRxChar(data);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    bSendThreadRunning = false;
                }
            }
        }

        public void interrupt() {
            bSendThreadRunning = false;
            Log.d(TAG, "SendThread interrupt bSendThreadRunning=" + bSendThreadRunning);
        }
    }

    public void receiveData(BluetoothGattCharacteristic characteristic) {
        if (characteristic == null)
            return;

        Log.d(TAG, "receiveData" );

        if (characteristic.getUuid().equals(DEV_TX_CHAR_UUID)) {
            byte[] data = characteristic.getValue();
            if (data != null) {
                Log.d(TAG, "receiveData, data=" + Utils.convertByteArray2HexStr(data, data.length));

                if(isSameAsLastData(data)) {
                    return;
                }
                updateLastRecvData(data);

                if (bleEventListener != null) {
                    bleEventListener.onDataReceived(data);
                }
            }
        }
    }

    private byte[] lastRecvData = null;
    private long lastRecvTime = 0;

    private void updateLastRecvData(byte[] data) {
        lastRecvData = data;
        lastRecvTime = System.currentTimeMillis();
    }

    private boolean isSameAsLastData(byte[] data) {
        if(lastRecvData == null)
            return false;

        if(System.currentTimeMillis() - lastRecvTime > 20)
            return false;

        if(data.length != lastRecvData.length)
            return false;

        for(int i = 0; i < lastRecvData.length; i++) {
            if(data[i] != lastRecvData[i])
                return false;
        }
        return true;
    }
}
